Skip to main content

Styling

Supacharger uses Tailwind CSS as its styling foundation. The root layout imports one CSS entrypoint:

import '@/supacharger/styles/globals.css';

That entrypoint loads Tailwind, the shared Supacharger styles, the developer-owned authentication presentation, and the application's general developer-owned stylesheet in that order. Do not import them again from the layout.

Style ownership

FileOwnerPurpose
src/supacharger/styles/globals.cssSupacharger CLITailwind entrypoint and import order only
src/supacharger/styles/supacharger.cssSupacharger CLIReusable Supacharger element and component rules
src/supacharger/styles/project.example.cssSupacharger CLIUnimported reference for the developer stylesheet
src/styles/supacharger-auth.cssApplication developerPresentation for managed authentication sc-auth-* hooks
src/styles/project.cssApplication developerProject theme tokens, global defaults, overrides, and product-specific classes

The CLI may replace files under src/supacharger/styles/ during a core update. It installs src/styles/supacharger-auth.css when absent, then preserves it alongside src/styles/project.css and src/supacharger.config.ts.

Keep the Project: ... header in project.css updated with the application name. This makes the ownership of copied or compared styles explicit.

Tailwind conventions

Use Tailwind utilities in markup for most styling. Add CSS only when a reusable semantic rule, an element default, a theme token, or a project-wide override is genuinely clearer than repeated utilities.

Place custom CSS in Tailwind's layers:

  • @layer base for project theme variables and element defaults;
  • @layer components for reusable semantic component classes; and
  • @layer utilities for small, single-purpose project utilities.

Tailwind's Preflight already supplies the normal reset through @import 'tailwindcss'. Do not reproduce Preflight rules in project CSS, and do not add another Tailwind import to project.css.

The merge-managed tailwind.config.ts retains its TypeScript filename, application font choices, and current CommonJS export. The exact-managed postcss.config.mjs uses an explicit ESM export so Next.js and Turbopack can evaluate the Tailwind PostCSS plugin reliably. Keep the @config reference unchanged and do not add a package-wide "type": "module" solely for either file.

Shared Supacharger rules

Rules that every Supacharger application should receive belong in the CLI-managed core stylesheet. Supacharger centrally gives enabled native and ARIA interactive controls a pointer cursor on hover:

src/supacharger/styles/supacharger.css
@layer base {
html {
scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
}

:where(
a[href],
button:not(:disabled),
input[type='button']:not(:disabled),
input[type='submit']:not(:disabled),
input[type='reset']:not(:disabled),
input[type='checkbox']:not(:disabled),
input[type='radio']:not(:disabled),
label[for],
select:not(:disabled),
summary,
[role='button']:not([aria-disabled='true']),
[role='link']:not([aria-disabled='true']),
[role='menuitem']:not([aria-disabled='true']),
[role='menuitemcheckbox']:not([aria-disabled='true']),
[role='menuitemradio']:not([aria-disabled='true']),
[role='option']:not([aria-disabled='true']),
[role='tab']:not([aria-disabled='true'])
) {
cursor: pointer !important;
}
}

The root rule is the native fallback for same-page fragment links. The installed root layout also mounts SmoothAnchorNavigation from src/supacharger/components/layout/smooth-anchor-navigation.tsx, because Next.js <Link> can otherwise perform an immediate fragment jump before CSS animates it. Together they make <a href="#features"> and <Link href="#features"> work automatically. Modified clicks, downloads, non-self targets, and missing fragments retain normal browser behaviour; add data-smooth-scroll="false" to opt out for one link. Visitors who request reduced motion receive immediate navigation. The !important cursor declaration intentionally keeps that interaction affordance authoritative when a component library supplies cursor-default. Disabled controls are excluded so they do not misleadingly advertise an available action. Ordinary components therefore should not repeat cursor-pointer or page-level smooth-scroll utilities.

Change this file in the canonical Supacharger core first, then distribute the same file through the CLI. Do not add product branding or application-specific selectors to it.

Project styles

Put the application's colors, typography defaults, visual effects, and overrides in src/styles/project.css:

src/styles/project.css
/**
* Project: Example Application
*
* Developer-owned. The Supacharger CLI must preserve this file.
*/

@layer base {
:root {
--primary: 174 49% 50%;
--primary-foreground: 222 47% 11%;
}
}

Tailwind color mappings in tailwind.config.ts consume these space-separated HSL channels. For example, #42bfb1 becomes 174 49% 50%.

--primary is the application's main brand action colour and --primary-foreground is the content colour placed on top of it. Prefer semantic utilities such as bg-primary, text-primary-foreground, text-foreground, text-muted-foreground, bg-accent, and ring-ring. They automatically follow the named project's light and dark token values; a shared component should not copy a product hex value.

Application-specific classes may override a shared Supacharger class because project.css is imported after supacharger.css. Keep overrides intentional and document why the consumer differs from the core.

Theme selector

The shared ModeToggle opens a three-position selector ordered System, Light, and Dark. Use appearance='marketing' beside the application-owned locale switcher when both controls appear in marketing navigation or a footer. The marketing appearance gives both triggers the same control height, padding, text size, and small radius. The application-owned root layout must mount a compatible theme provider with system mode enabled so every choice can resolve correctly.

Responsive SVG components

Keep UI SVGs under the developer-owned src/ path belonging to their feature or surface, with demo-only artwork under src/components/sc_demo/. Import them as React components through SVGR and use public/ only when an asset genuinely needs a URL.

Every responsive SVG must have a valid viewBox so it preserves its aspect ratio. When CSS or Tailwind controls the rendered size, remove width and height from the root <svg>:

<svg viewBox="0 0 323 46" ...>

Size the component at its call site with classes such as size-*, w-*, h-*, or size-[1em], and usually add shrink-0 beside text. Do not force mismatched dimensions or fix clipping with arbitrary component props. Use fill='currentColor' or stroke='currentColor' only when the artwork should inherit text colour.

Confirm that SVGR/SVGO preserved the rendered <svg> element's viewBox, test classes such as h-8 w-auto at the intended viewport sizes, and verify that the complete artwork stays within its bounds. Add a regression test that checks responsive SVG assets retain their viewBox and omit root-level intrinsic dimensions, then visually verify every affected page in the browser.

Inline loader branding

The primary application-owned inline loading animation lives at src/assets/svgr/ui/inline-loader.svg, with a matching black variant at src/assets/svgr/ui/inline-loader-dark.svg. Replace either file with a new SVG of the same name to customise it; Supacharger CLI updates preserve both. The shared loading component uses the primary asset.

This is an inline-loader convention for pending buttons, form actions, uploads, and compact content states. It does not replace an application's route-level or full-page loading design.

Render the asset as an SVGR component rather than with an image tag or public URL:

import InlineLoader from '@/assets/svgr/ui/inline-loader.svg';

<InlineLoader
aria-label='Saving'
className='size-5'
fill='currentColor'
role='status'
/>

The primary loader defaults to white and the dark clone defaults to black. Both use their root fill, so an SVGR caller can pass a different colour. A replacement may instead hard-code fill or stroke; in that case the same prop is harmless and the custom artwork keeps its own colours. Keep all UI SVGs under an appropriate developer-owned src/ path and import them through SVGR. Reserve public SVGs for favicon metadata, manifests, external URL contracts, and other cases that genuinely require a URL.

Colocate feature-owned SVGs with the developer-owned feature when that makes its lifecycle clearer. For example, demo-only logos belong under src/components/sc_demo/assets/ and are imported through SVGR, so deleting the replaceable demo also deletes its artwork.

SVG Backgrounds animated SVG preloaders is one source of replacement animations. Check and satisfy the selected asset's current licence and attribution requirements before publishing it.

Error and not-found pages

src/app/error/page.tsx, src/app/not-found.tsx, and their presentation under src/components/error-page/ are application-owned, CLI-preserved surfaces. This lets a product own its support destination, wording, signed-in chrome, and brand treatment without editing a protected Supacharger route.

The starter pattern verifies the current user on the server. A verified user sees the error state inside the normal authenticated header and footer; every other visitor sees only a vertically centred, full-viewport error state. The actions use the semantic project tokens described above. Keep the explicit /error route in PATH_AUTH_GUARD.UNAUTHED_USER.ALLOWED when AUTH_ONLY_APP is enabled, otherwise an authentication failure can be redirected away from the page intended to explain it. The canonical Proxy also lets /error bypass profile-onboarding redirects so an authenticated but incomplete account can still see the failure explanation.

Add translated wording under the developer-owned ErrorPage namespace. The error-copy resolver reads any available values directly and supplies protected English defaults for an absent namespace or missing field. This compatibility fallback prevents an older preserved catalogue—or a stale development module—from raising MISSING_MESSAGE inside the error boundary itself.

Fonts

Define application fonts with next/font in src/app/layout.tsx and expose them through a CSS variable:

import { Space_Grotesk } from 'next/font/google';

const spaceGrotesk = Space_Grotesk({
variable: '--font-space-grotesk',
subsets: ['latin'],
});

Apply the generated variable to the body class and map it in Tailwind configuration when it should back a font utility. Next.js downloads Google font assets at build time, so the browser does not request them from Google at runtime.

Favicon customisation

Use RealFaviconGenerator to create a complete favicon pack from the application's icon. A 300 × 300 px PNG works well as the source image. Configure the browser, mobile, and manifest options on the site, then download and extract the generated pack to a temporary folder.

Give your coding agent the location of the downloaded archive or extracted folder with a prompt like this:

Install the favicon pack from <path-to-downloaded-archive-or-folder> in this project. Inspect the existing favicon and metadata setup, replace the current favicon files and references with the new pack, and keep the implementation consistent with the framework and project structure. Remove obsolete favicon assets that have been replaced. Verify that the relevant metadata, manifest, and icon paths resolve correctly, then delete the temporary downloaded archive or extracted folder. Preserve unrelated files and summarise what changed.

Replace the placeholder with the pack's actual location. The pack does not need to be copied into the project first; it can remain in Downloads or another temporary location while the agent installs it.

Build Supacharger with SpecdriveKeep the specification, infrastructure and agent work in one project context.